package com.xingchi.tornado.log.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 数据脱敏工具类
 *
 * @author xiaoya
 * @date 2024/9/26 11:17
 */
@Slf4j
public class DataMaskingUtils {

    /**
     * 默认需要脱敏的字段名
     */
    private static final Set<String> SENSITIVE_FIELDS = new HashSet<>(Arrays.asList(
            "password", "oldPassword", "newPassword", "pwd", "passwd", "secret",
            "mobile", "phone", "telephone", "tel", "email", "mail",
            "idCard", "idNo", "cardNo", "bankCard", "bankCardNo", "creditCard",
            "realName", "name", "address", "home", "location"
    ));

    /**
     * 手机号码正则表达式
     */
    private static final Pattern MOBILE_PATTERN = Pattern.compile("1[3-9]\\d{9}");

    /**
     * 邮箱正则表达式
     */
    private static final Pattern EMAIL_PATTERN = Pattern.compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*");

    /**
     * 身份证正则表达式
     */
    private static final Pattern ID_CARD_PATTERN = Pattern.compile("\\d{17}[0-9Xx]|\\d{15}");

    /**
     * 银行卡正则表达式
     */
    private static final Pattern BANK_CARD_PATTERN = Pattern.compile("\\d{16,19}");

    public static final String PASSWORD = "password";

    /**
     * 脱敏JSON字符串中的敏感数据
     *
     * @param jsonStr       JSON字符串
     * @param excludeFields 排除的字段
     * @return 脱敏后的JSON字符串
     */
    public static String maskJsonString(String jsonStr, String... excludeFields) {
        if (!StringUtils.hasText(jsonStr)) {
            return jsonStr;
        }

        try {
            Set<String> excludeFieldSet = new HashSet<>();
            if (excludeFields != null && excludeFields.length > 0) {
                excludeFieldSet.addAll(Arrays.asList(excludeFields));
            }

            Object obj = JSON.parse(jsonStr);
            if (obj instanceof JSONObject) {
                maskJsonObject((JSONObject) obj, excludeFieldSet);
                return JSON.toJSONString(obj);
            } else if (obj instanceof Map) {
                Map<String, Object> map = new HashMap<>((Map<String, Object>) obj);
                maskMap(map, excludeFieldSet);
                return JSON.toJSONString(map);
            } else {
                return jsonStr;
            }
        } catch (Exception e) {
            log.warn("脱敏JSON数据失败: {}", e.getMessage());
            return jsonStr;
        }
    }

    /**
     * 脱敏JSONObject中的敏感数据
     *
     * @param jsonObject    JSONObject对象
     * @param excludeFields 排除的字段
     */
    private static void maskJsonObject(JSONObject jsonObject, Set<String> excludeFields) {
        for (String key : jsonObject.keySet()) {
            if (excludeFields.contains(key)) {
                continue;
            }

            Object value = jsonObject.get(key);
            if (value instanceof String) {
                String strValue = (String) value;
                if (SENSITIVE_FIELDS.contains(key.toLowerCase())) {
                    if (key.toLowerCase().contains(PASSWORD)) {
                        jsonObject.put(key, "******");
                    } else {
                        jsonObject.put(key, maskSensitiveInfo(strValue));
                    }
                } else {
                    // 尝试正则匹配敏感信息模式
                    jsonObject.put(key, maskByPattern(strValue));
                }
            } else if (value instanceof JSONObject) {
                maskJsonObject((JSONObject) value, excludeFields);
            } else if (value instanceof Map) {
                Map<String, Object> map = new HashMap<>((Map<String, Object>) value);
                maskMap(map, excludeFields);
                jsonObject.put(key, map);
            }
        }
    }

    /**
     * 脱敏Map中的敏感数据
     *
     * @param map           Map对象
     * @param excludeFields 排除的字段
     */
    private static void maskMap(Map<String, Object> map, Set<String> excludeFields) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            if (excludeFields.contains(key)) {
                continue;
            }

            Object value = entry.getValue();
            if (value instanceof String) {
                String strValue = (String) value;
                if (SENSITIVE_FIELDS.contains(key.toLowerCase())) {
                    if (key.toLowerCase().contains(PASSWORD)) {
                        map.put(key, "******");
                    } else {
                        map.put(key, maskSensitiveInfo(strValue));
                    }
                } else {
                    // 尝试正则匹配敏感信息模式
                    map.put(key, maskByPattern(strValue));
                }
            } else if (value instanceof JSONObject) {
                maskJsonObject((JSONObject) value, excludeFields);
            } else if (value instanceof Map) {
                Map<String, Object> subMap = new HashMap<>((Map<String, Object>) value);
                maskMap(subMap, excludeFields);
                map.put(key, subMap);
            }
        }
    }

    /**
     * 根据模式脱敏字符串
     *
     * @param str 待脱敏的字符串
     * @return 脱敏后的字符串
     */
    private static String maskByPattern(String str) {
        if (!StringUtils.hasText(str)) {
            return str;
        }

        String result = str;

        // 脱敏手机号
        Matcher mobileMatcher = MOBILE_PATTERN.matcher(result);
        StringBuffer sb = new StringBuffer();
        while (mobileMatcher.find()) {
            String mobile = mobileMatcher.group();
            mobileMatcher.appendReplacement(sb, maskMobile(mobile));
        }
        mobileMatcher.appendTail(sb);
        result = sb.toString();

        // 脱敏邮箱
        Matcher emailMatcher = EMAIL_PATTERN.matcher(result);
        sb = new StringBuffer();
        while (emailMatcher.find()) {
            String email = emailMatcher.group();
            emailMatcher.appendReplacement(sb, maskEmail(email));
        }
        emailMatcher.appendTail(sb);
        result = sb.toString();

        // 脱敏身份证
        Matcher idCardMatcher = ID_CARD_PATTERN.matcher(result);
        sb = new StringBuffer();
        while (idCardMatcher.find()) {
            String idCard = idCardMatcher.group();
            idCardMatcher.appendReplacement(sb, maskIdCard(idCard));
        }
        idCardMatcher.appendTail(sb);
        result = sb.toString();

        // 脱敏银行卡
        Matcher bankCardMatcher = BANK_CARD_PATTERN.matcher(result);
        sb = new StringBuffer();
        while (bankCardMatcher.find()) {
            String bankCard = bankCardMatcher.group();
            bankCardMatcher.appendReplacement(sb, maskBankCard(bankCard));
        }
        bankCardMatcher.appendTail(sb);
        result = sb.toString();

        return result;
    }

    /**
     * 脱敏敏感信息
     *
     * @param str 待脱敏的字符串
     * @return 脱敏后的字符串
     */
    private static String maskSensitiveInfo(String str) {
        if (!StringUtils.hasText(str)) {
            return str;
        }

        int length = str.length();
        if (length <= 1) {
            return "*";
        } else if (length <= 3) {
            return str.charAt(0) + "*".repeat(length - 1);
        } else {
            int maskLength = (int) Math.ceil(length * 0.6);
            int start = (length - maskLength) / 2;
            return str.substring(0, start) + "*".repeat(maskLength) + str.substring(start + maskLength);
        }
    }

    /**
     * 脱敏手机号
     *
     * @param mobile 手机号
     * @return 脱敏后的手机号
     */
    public static String maskMobile(String mobile) {
        if (!StringUtils.hasText(mobile) || mobile.length() != 11) {
            return mobile;
        }
        return mobile.substring(0, 3) + "****" + mobile.substring(7);
    }

    /**
     * 脱敏邮箱
     *
     * @param email 邮箱
     * @return 脱敏后的邮箱
     */
    public static String maskEmail(String email) {
        if (!StringUtils.hasText(email) || !email.contains("@")) {
            return email;
        }
        int atIndex = email.indexOf('@');
        String prefix = email.substring(0, atIndex);
        if (prefix.length() <= 3) {
            return prefix.charAt(0) + "**" + email.substring(atIndex);
        } else {
            return prefix.substring(0, 3) + "***" + email.substring(atIndex);
        }
    }

    /**
     * 脱敏身份证号
     *
     * @param idCard 身份证号
     * @return 脱敏后的身份证号
     */
    public static String maskIdCard(String idCard) {
        if (!StringUtils.hasText(idCard)) {
            return idCard;
        }
        int length = idCard.length();
        if (length != 15 && length != 18) {
            return idCard;
        }
        return idCard.substring(0, 6) + "********" + idCard.substring(length - 4);
    }

    /**
     * 脱敏银行卡号
     *
     * @param bankCard 银行卡号
     * @return 脱敏后的银行卡号
     */
    public static String maskBankCard(String bankCard) {
        if (!StringUtils.hasText(bankCard) || bankCard.length() < 10) {
            return bankCard;
        }
        return bankCard.substring(0, 4) + "****" + bankCard.substring(bankCard.length() - 4);
    }
}
